#pragma once

#include <carbon/defs.h>
#include "detail/rev_iter.h"
#include "detail/alloc.h"
#include <utility>
#include <memory>

CARBON_NS_BEGIN
namespace detail {

template<class T, class Alloc>
struct VectorImpl {
    T*          m_data;
    size_t      m_size;
    size_t      m_capacity;
    Alloc       m_alloc;

    VectorImpl(const Alloc & alloc) : m_data(nullptr), m_size(0), m_capacity(0), m_alloc(alloc) {}

    VectorImpl(size_t size, const Alloc & alloc) : m_data(nullptr), m_size(0), m_capacity(0), m_alloc(alloc) 
    {
        reserve(size);
    }

    VectorImpl(size_t size, const T& data, const Alloc & alloc) : m_data(nullptr), m_size(0), m_capacity(0), m_alloc(alloc)
    {
        reserve(size);
        for (size_t i = 0; i < size; ++i) push_back(data);
    }

    VectorImpl(const VectorImpl& other) = default;

    VectorImpl(VectorImpl&& other)
    : m_data (other.m_data)
    , m_size (other.m_size)
    , m_capacity (other.m_capacity)
    , m_alloc (other.m_alloc)
    {
        other.m_data = nullptr;
        other.m_size = 0;
        other.m_capacity = 0;
    }

    ~VectorImpl() CRANE_NO_EXCEPT {
        for ( size_t i = 0; i < m_size; ++i ) m_alloc.destroy(m_data + i);
        if ( m_data ) m_alloc.deallocate(m_data, m_capacity);
        m_data = nullptr;
        m_size = 0;
        m_capacity = 0;
    }

    T * begin() { return m_data; }
    T * end() { return m_data + m_size; }
    const T * cbegin() const { return m_data; }
    const T * cend() const { return m_data + m_size; }

    // 重新分配空间, 确保至少有n个元素
    void reserve(size_t n) {
        if ( n <= m_capacity ) return ;

        T * new_data = m_alloc.allocate(n);
        for ( size_t i = 0; i < m_size; ++i ) {
            m_alloc.construct(new_data + i, std::move(m_data[i]));
        }
        m_alloc.deallocate(m_data, m_capacity);
        m_capacity = n;
        m_data = new_data;
    }

    void popBack(size_t n) {
        while ( n > 0 ) {
            --m_size;
            m_alloc.destroy(m_data + m_size);
            --n;
        }
    }

    void pushBack(size_t n, const T& data) {
        if ( m_capacity - m_size < n ) this->reserve(m_size + n);
        for ( size_t i = 0; i < n; ++i ) m_alloc.construct(m_data + m_size++, std::move(data));
    }

    void pushBack(T&& data) {
        if ( m_size == m_capacity ) this->reserve(m_size + 1);
        m_alloc.construct(m_data + m_size++, std::move(data));
    }


    void swap(VectorImpl & other) {
        VectorImpl tmp(*this);
        *this = other;
        other = tmp;
    }

    void clear() {
        if ( m_size == 0 ) return ;
        T * p = m_data;
        T * p1 = m_data + m_size;
        while ( p != p1 ) m_alloc.destroy(p++);
        m_size = 0;
    }
};


} // namespace detail

template<class T, class Alloc = Allocator<T> >
class Vec {
public:
    using ValueType  = T;
    using Allocator  = Alloc;
    using Iterator   = T*;
    using ConstIterator   = const T*;
    using RevIterator = RevIterator<Iterator>;
    using RevConstIterator = RevIterator<ConstIterator>;

private:
    detail::VectorImpl<T, Alloc> m_impl;

public:
    Vec() : m_impl(Allocator()) {}
    
    Vec(const Alloc& alloc) : m_impl(alloc) {}

    Vec(size_t size) : m_impl(size, Allocator()) {}

    Vec(size_t size, const Alloc& alloc) : m_impl(size, alloc) {}

    Vec(size_t size, const T& value) : m_impl(size, value, Allocator()) {}

    Vec(size_t size, const T& value, const Alloc& alloc) : m_impl(size, value, alloc) {}

    Vec(const Vec& other) : m_impl(other.m_impl) {}

    Vec(Vec && other) : m_impl(std::move(other.m_impl)) {}

    ~Vec() {}

    Vec & operator=(const Vec& other);
    Vec & operator=(Vec && other);

    T & operator[](size_t index) { return m_impl.m_data[index]; }
    const T & operator[](size_t index) const { return m_impl.m_data[index]; }

    size_t size() const { return m_impl.m_size; }
    size_t capacity() const { return m_impl.m_capacity; }
    bool empty() const { return m_impl.m_size == 0; }

    T & at(size_t i) { return m_impl.m_data[i]; }
    const T & at(size_t i) const { return m_impl.m_data[i]; }

    T * data()      { return m_impl.m_data; }
    T & front()     { return *m_impl.begin(); }
    T & back()      { return *(m_impl.end() - 1); }

    const T * data() const { return m_impl.m_data; }
    const T& front() const { return *m_impl.begin(); }
    const T& back() const  { return *(m_impl.end() - 1); }

    Iterator begin() { return m_impl.begin(); }
    Iterator end() { return m_impl.end(); }

    ConstIterator begin() const  { return m_impl.cbegin(); }
    ConstIterator end() const { return m_impl.cend(); }


    ConstIterator cbegin() const { return m_impl.cbegin(); }
    ConstIterator cend() const { return m_impl.cend(); }

    Vec& clear() { m_impl.clear(); return *this; }

    Vec& reserve(size_t new_cap) { m_impl.reserve(new_cap); return *this; }
    
    Vec& resize(size_t new_size) { return this->resize(new_size, T()); }
    Vec& resize(size_t new_size, const T & val);

    Vec& shrink();

    Vec& pushBack(const T& val);
    Vec& pushBack(T&& val);

    Vec& popBack();
    
    void swap(Vec & other) { m_impl.swap(other.m_impl); }

}; // namespace crane


template<typename T, typename Alloc>
Vec<T, Alloc> & Vec<T, Alloc>::operator=(const Vec & other) {
    detail::VectorImpl<T, Alloc> tmp(other.m_impl);
    m_impl.swap(tmp);
}

template<typename T, typename Alloc>
Vec<T, Alloc> & Vec<T, Alloc>::operator=(Vec && other) {
    detail::VectorImpl<T, Alloc> tmp(std::move(this->m_impl));
    this->m_impl->swap(other.m_impl);
    return *this;
}


template<typename T, typename Alloc>
Vec<T, Alloc> &  Vec<T, Alloc>::resize(size_t new_size, const T & val) {
    if ( new_size < size() ) {
        m_impl->popBack(size() - new_size);
    } else if ( new_size > size() ) {
        this->reserve(new_size);
        m_impl->pushBack(new_size - size(), val);
    }
}

template<typename T, typename Alloc>
Vec<T, Alloc> &  Vec<T, Alloc>::shrink() {
    if ( this->size() < this->capacity() ) {
        detail::VectorImpl<T, Alloc> tmp(m_impl.m_alloc);
        tmp.reserve(this->size());

        T * p1 = m_impl.m_data;
        T * p2 = m_impl.end();
        T * p0 = tmp.m_data;
        
        while ( p1 != p2 ) {
            tmp.pushBack(std::move(*p1));
            ++p1;
        }

        m_impl.swap(tmp);
    }
    return *this;
}


template<typename T, typename Alloc>
Vec<T, Alloc> & Vec<T,Alloc>::pushBack(const T & val) {
    if ( this->size() == this->capacity() ) this->reserve(this->size() + 1);
    m_impl.pushBack(1, val);
    return *this;
}

template<typename T, typename Alloc>
Vec<T, Alloc>& Vec<T,Alloc>::pushBack(T && val) {
    if ( this->size() == this->capacity() ) this->reserve(this->size() + 1);
    m_impl.pushBack(std::move(val));
    return *this;
}


template<typename T, typename Alloc>
Vec<T, Alloc> & Vec<T,Alloc>::popBack() {
    if ( !m_impl.empty() ) {
        T * p = m_impl.end();
        m_impl.m_alloc.destroy(p);
        m_impl.m_size -= 1;
    }
    return *this;
}


CARBON_NS_END 
